#!/usr/bin/python

# Copyright (C) 2009  Joao Neves
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

import re
import urllib2
from xml.dom import minidom

from waveapi import events
from waveapi import model
from waveapi import robot
from waveapi.document import Range

WIKIFY_EXPR = re.compile('(?<=\<wikify )(?P<text>[\(\)\s\w]+)\>', re.UNICODE)
WIKIDEF_EXPR = re.compile('(?<=\<wikidef )(?P<text>[\(\)\s\w]+)\>', re.UNICODE)
WIKIFY_ALL = re.compile('\<wikify [\(\)\s\w]+\>', re.UNICODE)
WIKIDEF_ALL = re.compile('\<wikidef [\(\)\s\w]+\>', re.UNICODE)
WIKISEARCH = "http://en.wikipedia.org/w/api.php?action=opensearch&search=%s&format=xml"

def getURL(string, query):
	repl = re.sub('%20', ' ', query)
	doc = minidom.parseString(string)
	items = doc.getElementsByTagName("Item")

	for item in items:
		text = item.getElementsByTagName("Text")
		if text[0].childNodes[0].nodeValue.lower() == repl.lower() or len(items) == 1:
			url = item.getElementsByTagName("Url")
			return url[0].childNodes[0].nodeValue

def placeLinks(text):
	"""Replaces text marked up by 'wikify:' with a link
	to the corresponding Wikipedia article."""

	newtext = text
	matches = re.findall(WIKIFY_EXPR, text)
	tagged = re.findall(WIKIFY_ALL, text)
	idx = 0

	for match in matches:
		text = re.sub(' ', '%20', match)
		sock = urllib2.urlopen(WIKISEARCH % text)
		data = sock.read()
		sock.close()
		#repl = re.sub(' ', '_', match)
		#url = "(http://en.wikipedia.org/wiki/%s)" % (repl)
		url = getURL(data, match)
		if url is not None:
			url = "(%s)" % url
			linked = "%s %s" % (match, url)
			newtext = re.sub(tagged[idx], linked, newtext)
		else:
			newtext = re.sub(tagged[idx], "%s [no Wikipedia page for this]" % tagged[idx], newtext)
		idx = idx + 1

	return newtext

def getDefinition(string, query):
	repl = re.sub('%20', ' ', query)
	doc = minidom.parseString(string)
	items = doc.getElementsByTagName("Item")

	for item in items:
		text = item.getElementsByTagName("Text")
		if text[0].childNodes[0].nodeValue.lower() == repl.lower() or len(items) == 1:
			definition = item.getElementsByTagName("Description")
			return definition[0].childNodes[0].nodeValue

def placeDefs(text):
	"""Places Wikipedia definitions in the text."""
	newtext = text
	matches = re.findall(WIKIDEF_EXPR, text)
	tagged = re.findall(WIKIDEF_ALL, text)
	idx = 0

	for match in matches:
		text = re.sub(' ', '%20', match)
		sock = urllib2.urlopen(WIKISEARCH % text)
		data = sock.read()
		sock.close()
		definition = getDefinition(data, match)
		if definition is not None:
			definition = "(Wikipedia - %s)" % definition
			defined = "%s %s" % (match, definition)
			newtext = re.sub(tagged[idx], defined, newtext)
		else:
			newtext = re.sub(tagged[idx], "%s [no Wikipedia entry for this]" % tagged[idx], newtext)
		idx = idx + 1

	return newtext


def OnBlipSubmitted(properties, context):
	"""Invoked when a Blip is submitted to the wave."""
	blip = context.GetBlipById(properties['blipId'])
	contents = blip.GetDocument().GetText()
	
	newcontents = placeLinks(contents)
	newcontents = placeDefs(newcontents)

	if newcontents != contents:
		blip.GetDocument().SetText(newcontents)

	#remaining = re.finditer(WIKIFY_ALL, newcontents)

	#for wtext in remaining:
	#	range = Range(*wtext.span())
	#	document.setStyle(range, StyleType.ITALIC) #Not supported yet


def OnDocumentChanged(properties, context):
	"""Invoked when the document is edited."""
	pass

def OnRobotAdded(properties, context):
	"""Invoked when the robot has been added."""
	root_wavelet = context.GetRootWavelet()
	root_wavelet.CreateBlip().GetDocument().SetText("Hi! I can add Wikipedia link for topics by typing <wikify topic>. Additionally, I can insert full definitions using <wikidef topic>. ;)")

if __name__ == '__main__':
	myRobot = robot.Robot('Wikify',
			image_url='http://wikifier.appspot.com/assets/icon.png',
			version='2',
			profile_url='http://wikifier.appspot.com/assets/profile.html')
	myRobot.RegisterHandler(events.BLIP_SUBMITTED, OnBlipSubmitted)
	myRobot.RegisterHandler(events.DOCUMENT_CHANGED, OnDocumentChanged)
	myRobot.RegisterHandler(events.WAVELET_SELF_ADDED, OnRobotAdded)
	myRobot.Run()
